#ifndef INC_VECTOR3_H_
#define INC_VECTOR3_H_

// This will define in Win32 the family of macros M_PI, .. 
#define _USE_MATH_DEFINES
#include <cmath>
#include <cstring>
#include <cstdlib>

namespace gti
{

template<class T>
class vector2 
{ 
    public:
        vector2();
		vector2(T X, T Y);
		template<class T2>
			vector2(const T2* vec) : x(vec[0]), y(vec[1])
		{
		}

    union
    {
        struct{
            T x,y;
        };
        struct{
            T u,v;
        };
        struct{
            T from,count;
        };
        T t[2];
    };
		const T  &operator[](unsigned int i) const {return t[i];}
		T  &operator[](unsigned int i) {return t[i];}
		const T  &operator[](int i) const {return t[i];}
		T  &operator[](int i) {return t[i];}

		void set(T iu, T iv){ u = iu; v = iv;}
		void parseFromText(const char* text);

		// useful methods
		T normalize();
		T length() const;
		T squaredLength() const;

		operator T *(void) {return t;}
		operator const T *(void) const {return t;}

    vector2<T>& operator+=(const vector2<T>& v)
    {
	    t[0] += v.t[0];
	    t[1] += v.t[1];
	    return *this;
    }

		vector2<T>& operator-=(const vector2<T>& v)
    {
	    t[0] -= v.t[0];
	    t[1] -= v.t[1];
	    return *this;
    }
    void operator *= ( const T &s )
		{
      t[0] *= s;
      t[1] *= s;
    }
    void operator /= ( const T &s )
    {
        T aux = 1/s;
        t[0] *= s;
        t[1] *= s;
    }

    vector2<T> operator*(const T& v) const
    {
	    return vector2<T>(x*v,y*v);
    }
    
    vector2<T> operator*(const vector2<T>& v) const
    {
	    return vector2<T>(x*v.x,y*v.y);
    }
    
    vector2<T> operator/(const T& v) const
    {
        T aux = 1/v;
	    return vector2<T>(x*aux,y*aux);
    }
    
		vector2<T> operator+(const vector2<T>& v) const
    {
	    return vector2<T>(x+v.x,y+v.y);
    }

		vector2<T> operator-(const vector2<T>& v) const
    {
	    return vector2<T>(x-v.x,y-v.y);
    }
};

template <class T>
vector2<T>::vector2()
{
}

template <class T>
vector2<T>::vector2(T X, T Y): u(X),  v(Y)
{
}

template <class T>
void vector2<T>::parseFromText(const char* text)
{
	int pos = 0;
	char num[255];
	const char* start = text;
	const char* current = text;

	while(1)
	{
		if (*current == ',' || (*current == '\0' && current != text) )
		{
			strncpy(num, start, current - start);
			num[current - start] = '\0';
			start = current + 1;
			switch(pos)
			{
				case 0: x = (T)atof(num); break;
				case 1: y = (T)atof(num); break;
				default: return; break;
			}
			++pos;
			if (*current == '\0')
				break;
		}

		++current;
	}
}

template <class T>
T vector2<T>::length() const
{
	return (T)(sqrt(x*x+y*y));
}

template <class T>
T vector2<T>::squaredLength() const
{
	return (x*x + y*y);
}

template <class T>
T vector2<T>::normalize()
{
	T norma;
  T l = squaredLength();

	if (fabs(l)<0.0000001)
	{
		return (T)0.0;
	}
	l = (T)sqrt(l);

	norma = (T) 1.0 / l;
	x *= norma;
	y *= norma;
	return l;
}


//*******************

template<class T>
class vector4
{ 
    public:
        vector4() {}
		vector4(T ix, T iy, T iz, T iw): x(ix), y(iy), z(iz), w(iw) {}
    union
    {
        struct{
            T x,y,z,w;
        };

        T t[4];
    };
		const T  &operator[](unsigned int i) const {return t[i];}
		T  &operator[](unsigned int i) {return t[i];}
		const T  &operator[](int i) const {return t[i];}
		T  &operator[](int i) {return t[i];}

		void parseFromText(const char* text);
		void set(T ix, T iy, T iz, T iw){ x = ix; y = iy; z = iz; w = iw;}

		operator T *(void) {return t;}
		operator const T *(void) const {return t;}

    void operator *= ( const T &s ) {
      t[0] *= s;
      t[1] *= s;
      t[2] *= s;
      t[3] *= s;
    }
};

template <class T>
class vector3
{
public:
	vector3();
	vector3(T X, T Y, T Z);
	vector3(const vector3<T>& vec);
	vector3(const T* f);

	template<class T2>
		vector3(const T2* vec) : x((T)vec[0]), y((T)vec[1]), z((T)vec[2])
	{
	}

	template<class T2>
		vector3(const vector3<T2>& vec) : x((T)vec.x), y((T)vec.y), z((T)vec.z)
	{
	}

public:
	union
	{
		struct {T x; T y; T z;};
		T v[3];
	};

	void set(const T* xyz);
	void set(T X, T Y, T Z);
	// useful methods
	T normalize();
	T length() const;
	T squaredLength() const;
	void abs();
	void parseFromText(const char* text);

	// computes the nearest aligned axis with the vector
	vector3<T> getNearestAlignedAxis() const;

	void lerp(const vector3<T>& b, T t);
	void lerp(const vector3<T>& b, T t, vector3<T>& c) const;

// useful operators

	const T &operator[](int i) const {return v[i];}
	T &operator[](int i) {return v[i];}
	const T &operator[](unsigned int i) const {return v[i];}
	T &operator[](unsigned int i) {return v[i];}

	const T &operator()(int i) const {return v[i];}
	T &operator()(int i) {return v[i];}

	operator T*() {return v;}
	operator const T*() const {return v;}

	vector3<T>& operator += (const vector3<T>& vec);
	vector3<T>& operator -= (const vector3<T>& vec);
	vector3<T>& operator -= (T f);
	vector3<T>& operator *= (T f);
	vector3<T>& operator /= (T f);
	
	vector3<T> operator -() const;
	vector3<T> operator-(const vector3<T>& vec) const;
	vector3<T> operator+(const vector3<T>& vec) const;
	vector3<T> operator*(const vector3<T>& vec) const;
	vector3<T> operator*(T factor) const;
	vector3<T> operator/(T factor) const;
};

template <class T>
vector3<T>::vector3()
{
}

template <class T>
vector3<T>::vector3(T X, T Y, T Z): x(X),  y(Y), z(Z)
{
}

template <class T>
vector3<T>::vector3(const vector3<T>& vec): x(vec.x),  y(vec.y), z(vec.z)
{
}

template <class T>
vector3<T>::vector3(const T* f): x(f[0]),  y(f[1]), z(f[2])
{
}

/*
template <class T, class T2>
vector3<T>& operator = (vector3<T>& vec, const vector3<T2>& vec2)
{
	vec.x = vec2.x;
	vec.y = vec2.y;
	vec.z = vec2.z;
    return vec;
}
*/

template <class T>
void vector3<T>::set(T X, T Y, T Z)
{
	x= X; y=Y; z=Z;
}

template <class T>
void vector3<T>::set(const T* xyz)
{
	memcpy(this,xyz,sizeof(vector3<T>));
}

template <class T>
T vector3<T>::length() const
{
	return (T)(sqrt(x*x+y*y+z*z));
}

template <class T>
T vector3<T>::squaredLength() const
{
	return (x*x + y*y + z*z);
}

template <class T>
void vector3<T>::abs()
{
	x = (T)fabs(x);
	y = (T)fabs(y);
	z = (T)fabs(z);
}

template <class T>
void vector3<T>::lerp(const vector3<T>& b, T t)
{
	const vector3<T>& dir = b - (*this);
	(*this) += dir*t;
}

template <class T>
void vector3<T>::lerp(const vector3<T>& b, T t, vector3<T>& c) const
{
	c = (*this) + (b-(*this))*t;
}

template <class T>
T vector3<T>::normalize()
{
	T norma;
  T l = squaredLength();

	if (fabs(l)<0.0000001)
	{
		return (T)0.0;
	}
	l = (T)sqrt(l);

	norma = (T) 1.0 / l;
	x *= norma;
	y *= norma;
	z *= norma;
	
	return l;
}

template <class T>
void vector3<T>::parseFromText(const char* text)
{
	int pos = 0;
	char num[255];
	const char* start = text;
	const char* current = text;

	while(1)
	{
		if (*current == ',' || (*current == '\0' && current != text) )
		{
			strncpy(num, start, current - start);
			num[current - start] = '\0';
			start = current + 1;
			switch(pos)
			{
				case 0: x = (T)atof(num); break;
				case 1: y = (T)atof(num); break;
				case 2: z = (T)atof(num); break;
				default: return; break;
			}
			++pos;
			if (*current == '\0')
				break;
		}

		++current;
	}
}

template <class T>
void vector4<T>::parseFromText(const char* text)
{
	int pos = 0;
	char num[255];
	const char* start = text;
	const char* current = text;

	while(1)
	{
		if (*current == ',' || (*current == '\0' && current != text) )
		{
			strncpy(num, start, current - start);
			num[current - start] = '\0';
			start = current + 1;
			switch(pos)
			{
				case 0: x = (T)atof(num); break;
				case 1: y = (T)atof(num); break;
				case 2: z = (T)atof(num); break;
				case 3: w = (T)atof(num); break;
				default: return; break;
			}
			++pos;
			if (*current == '\0')
				break;
		}

		++current;
	}
}

template <class T>
vector3<T>& vector3<T>::operator+=(const vector3<T>& v)
{
	x+=v.x;
	y+=v.y;
	z+=v.z;
	return *this;
}

template <class T>
vector3<T>& vector3<T>::operator-=(const vector3<T>& v)
{
	x-=v.x;
	y-=v.y;
	z-=v.z;
	return *this;
}

template <class T>
vector3<T>& vector3<T>::operator-=(T f)
{
	x-=f;
	y-=f;
	z-=f;
	return *this;
}

template <class T>
vector3<T>& vector3<T>::operator *= (T f)
{
	x *= f;
	y *= f;
	z *= f;
	return *this;
}

template <class T>
vector3<T>& vector3<T>::operator /= (T f)
{
	T d = 1.0f/f;
	x *= d;
	y *= d;
	z *= d;
	return *this;
}

template <class T>
vector3<T> vector3<T>::operator+(const vector3<T>& vec) const
{
  return vector3<T>(x+vec.x,y+vec.y,z+vec.z);
}

//Warning: Component product, no cross product
template <class T>
vector3<T> vector3<T>::operator*(const vector3<T>& vec) const
{
  return vector3<T>(x*vec.x,y*vec.y,z*vec.z);
}

template <class T>
vector3<T> vector3<T>::operator -() const
{
	return vector3<T>(-x, -y, -z);
}

template <class T>
vector3<T> vector3<T>::operator-(const vector3<T>& vec) const
{
  return vector3<T>(x-vec.x, y-vec.y, z-vec.z);
}

template <class T>
vector3<T> vector3<T>::operator*(T f) const
{
   return vector3<T>(x*f,y*f,z*f); 
}

template <class T>
vector3<T> vector3<T>::operator/(T f) const
{
	T invf = (T)1.0/f;
   return vector3<T>(x*invf,y*invf,z*invf); 
}

template <class T>
vector3<T> operator*(T f,const vector3<T>& vec)
{
  return vector3<T>(vec.x*f,vec.y*f,vec.z*f); 
}

template <class T>
vector3<T> operator/(T f, const vector3<T>& vec)
{
  T d=(T)1.0/f;
  return vector3<T>(vec.x*d,vec.y*d,vec.z*d); 
}

template <class T>
T DotProduct(const vector3<T> &v1, const vector3<T> &v2)
{
  return (v1.x*v2.x+v1.y*v2.y+v1.z*v2.z);
}

template <class T>
vector3<T> CrossProduct(const vector3<T> &v1, const vector3<T> &v2)
{
  return vector3<T>((v1.y * v2.z) - (v1.z * v2.y),(v1.z * v2.x) - (v1.x * v2.z),(v1.x * v2.y) - (v1.y * v2.x));
}

template <class T>
vector3<T> ScalarProduct(const vector3<T> &v1, const vector3<T> &v2)
{
	return vector3<T>(v1.x*v2.x,v1.y*v2.y,v1.z*v2.z);
}


/*
//TODO: fix skeletal animation
template <class T>
vector3<T> operator*(const vector3<T>& vec, const quaternion& q)
{
	vector3<T> ret;
	quaternion temp(-q.x, -q.y, -q.z, q.w);
	temp *= vec;
	temp *= q;

	ret.x = temp.x;
	ret.y = temp.y;
	ret.z = temp.z;

	return ret;
}

template <class T>
void vector3<T>::operator*=(const quaternion& q)
{
  quaternion temp(-q.x, -q.y, -q.z, q.w);
  temp *= *this;
  temp *= q;

  x = temp.x;
  y = temp.y;
  z = temp.z;
}
*/
// computes the nearest aligned axis with the vector

template <class T>
vector3<T> vector3<T>::getNearestAlignedAxis() const
{
	vector3<T> aa;

	int flagu = 0;
	flagu = (fabs(x) > fabsf(y)) ? 0 : 1;
	if (fabs(z) > fabs(v[flagu])) flagu = 2;

	aa.set(0,0,0);
	aa[flagu] = (v[flagu]>0.0f) ? 1.0f : -1.0f;

	return aa;
}


typedef vector2<int> vector2i;
typedef vector2<float> vector2f;
typedef vector3<float> vector3f;
//typedef vector3<unsigned int> vector3ui;
typedef vector3<unsigned short int> vector3us;
typedef vector4<float> vector4f;
typedef vector3<unsigned char> vector3ub;

};//namespace gti

typedef gti::vector2<float> Vector2;
typedef gti::vector3<float> Vector3;
typedef gti::vector4<float> Vector4;

#endif//INC_VECTOR_H

